package com.ld.security.shop.crawler.modules.wo.service.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.gargoylesoftware.htmlunit.WebClient;
import com.ld.security.common.core.exception.ApiException;
import com.ld.security.common.core.util.ReflectionUtil;
import com.ld.security.common.core.util.StringUtil;
import com.ld.security.common.core.util.UuidUtil;
import com.ld.security.common.mybatis.base.po.IdPo;
import com.ld.security.common.mybatis.base.po.IdsPo;
import com.ld.security.common.mybatis.constant.SqlCondition;
import com.ld.security.common.mybatis.core.wrapper.EntityWrapper;
import com.ld.security.shop.crawler.modules.wo.constant.*;
import com.ld.security.shop.crawler.modules.wo.entity.*;
import com.ld.security.shop.crawler.modules.wo.mapping.IWoTaskMapping;
import com.ld.security.shop.crawler.modules.wo.po.TaskDispatchPo;
import com.ld.security.shop.crawler.modules.wo.service.*;
import com.ld.security.common.mybatis.base.service.*;
import org.apache.http.conn.HttpHostConnectException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

@Service
@Transactional
class WoTaskServiceImpl extends ISuperServiceImpl<WoTask> implements
        IWoTaskService {

    @Autowired
    IWoTaskMapping iWoTaskMapping;

    @Autowired
    IWoIpService iWoIpService;

    @Autowired
    IWoParseService iWoParseService;

    @Autowired
    IWoLogService iWoLogService;

    @Override
    public List<WoTask> findByStatus(WoTaskConstant.status status) {
        WoTask e = this.newInstance();
        e.setStatus(status);
        return this.all(e);
    }

    private void parse(WoTask task){
        WoLog woLog =  iWoLogService.newInstance();
        woLog.setTaskId(task.getId());
        woLog.putJsonKey("taskId",task.getLabel());
        try {
            task.setStatus(WoTaskConstant.status.finish);
            WoParse woParse =iWoParseService.findByMatchUrl(task.getUrl());
            String content;
            if(woParse.getUrlType() == WoParseConstant.urlType.html){
                content =  iWoIpService.staticHtml(task.getUrl());
            }else {
                content =   iWoIpService.dynamicHtml(task.getUrl());
            }
            if(StringUtil.isNull(content)){
                ApiException.throw400("爬取界面失败");
            }
            task.setContent(content);
            iWoParseService.parse(task);
            woLog.setLogType(WoLogConstant.logType.task_success);
            woLog.setLabel("任务执行成功:"+task.getLabel());
            woLog.setContent("任务完成");
        }catch (Exception e){
            e.printStackTrace();
            woLog.setLabel("任务执行异常:"+e.getMessage());
            String content = ExceptionUtil.stacktraceToString (e);
            woLog.setContent(content);
            task.setStatus(WoTaskConstant.status.error);
            woLog.setLogType(WoLogConstant.logType.task_error);
            if(e instanceof HttpHostConnectException ){
                woLog.setLabel("ip异常:"+e.getMessage());
                HttpHostConnectException hc = (HttpHostConnectException)e;
                WoIp woIp = new WoIp();
                woIp.setHost(hc.getHost().getHostName());
                woIp.setPort(hc.getHost().getPort());
                woIp.setStatus(WoIpConstant.status.lock);
                iWoIpService.save(woIp);
            }
        }
        iWoLogService.save(woLog);
        this.save(task);


    }

    private long parse(List<WoTask> list) {

        CompletableFuture[] cs = new CompletableFuture[list.size()];
        int x = 0;
        for (WoTask woTask : list) {
            cs[x++]= CompletableFuture.supplyAsync(() -> {
                this.parse(woTask);
                return true;
            });
        }
        CompletableFuture.allOf(cs).whenComplete(new BiConsumer(){
            @Override
            public void accept(Object o, Object o2) {
                parseAfter(list);
            }
        }).join();
        return list.stream().filter(val -> val.getStatus() == WoTaskConstant.status.finish).count();
    }

    @Autowired
    IWoGroupService iWoGroupService;




    private void parseAfter(List<WoTask> list){
        Map<String,List<WoTask>> listMap = list.stream().collect(Collectors.groupingBy(val -> val.getGroupId()));
        listMap.forEach((x,y) -> {
            List<String>  finishUrls = y.stream()
                    .filter(val -> val.getStatus() == WoTaskConstant.status.finish)
                    .map(val -> val.getUrl())
                    .collect(Collectors.toList());
            List<String>  errorUrls= y.stream()
                    .filter(val -> val.getStatus() == WoTaskConstant.status.error)
                    .map(val -> val.getUrl())
                    .collect(Collectors.toList());
            WoLog woLog =  iWoLogService.newInstance();
            woLog.setLabel("任务组执行"+y.size()+"任务,共"+finishUrls.size()+"个执行成功");
            if(finishUrls.size() == y.size()){
                iWoGroupService.updateStatus(x,WoGroupConstant.status.success);
                woLog.setContent("执行成功的任务:"+finishUrls);
                woLog.setLogType(WoLogConstant.logType.group_success);
            }else{
                iWoGroupService.updateStatus(x,WoGroupConstant.status.error);
                woLog.setContent("执行失败的任务:"+errorUrls);
                woLog.setLogType(WoLogConstant.logType.group_error);
            }
            iWoLogService.save(woLog);
        });
    }



    /**
     * 异步执行解析
     * @param list
     */
    private void syncParse(List<WoTask> list) {
        CompletableFuture[] cs = new CompletableFuture[list.size()];
        int x = 0;
        for (WoTask woTask : list) {
            cs[x++] = CompletableFuture.supplyAsync(() -> {
                this.parse(woTask);
                return woTask;
            });
        }


        CompletableFuture.allOf(cs).whenComplete(new BiConsumer(){
            @Override
            public void accept(Object o, Object o2) {
                parseAfter(list);
            }
        });
    }

    @Override
    public synchronized WoTask parseSave(WoTask entity) {

        String id = UuidUtil.getId(entity.getUrl());
        entity.setId(id);
        IdPo po = new IdPo(id);
        if(this.count(po) == 0){
            super.save(entity);
        }

        return entity;
    }

    @Override
    public int save(WoTask entity) {
        String id = UuidUtil.getId(entity.getUrl());
        entity.setId(id);
        return super.save(entity);
    }

    @Override
    public long dispatch(TaskDispatchPo id) {
        List<WoTask> woTask =  this.all(id);
       return this.parse(woTask);
    }

    @Override
    public long syncDispatch(TaskDispatchPo id) {
        List<WoTask> woTask =  this.all(id);

        if(!woTask.isEmpty()){
            WoTask e =  this.newInstance();

            e.setStatus(WoTaskConstant.status.run);
            EntityWrapper ew =  this.getWrapper(id);
            iWoTaskMapping.update(e,ew);

        }

        this.syncParse(woTask);
        return woTask.size();
    }

    @Override
    public boolean dispatch(String id) {
        WoTask woTask = this.getById(id);
        String content =  iWoIpService.dynamicHtml(woTask.getUrl());
        woTask.setContent(content);

        this.parse(woTask);
        return true;
    }
}
